
#include "itkImageToImageFilter.h"
#include "itkBinaryReconstructionByErosionImageFilter.h"
#include "itkImageRegionExclusionIteratorWithIndex.h"
#include "itkProgressAccumulator.h"

namespace itk {

template<class TInputImage>
class ITK_EXPORT BinaryFillholeImageFilter : 
    public ImageToImageFilter<TInputImage, TInputImage>
{
public:

  typedef BinaryFillholeImageFilter Self;
  typedef ImageToImageFilter<TInputImage, TInputImage>
  Superclass;
  typedef SmartPointer<Self>        Pointer;
  typedef SmartPointer<const Self>  ConstPointer;

  typedef TInputImage InputImageType;
  typedef TInputImage OutputImageType;
  typedef typename InputImageType::Pointer         InputImagePointer;
  typedef typename InputImageType::ConstPointer    InputImageConstPointer;
  typedef typename InputImageType::RegionType      InputImageRegionType;
  typedef typename InputImageType::PixelType       InputImagePixelType;
  typedef typename OutputImageType::Pointer        OutputImagePointer;
  typedef typename OutputImageType::ConstPointer   OutputImageConstPointer;
  typedef typename OutputImageType::RegionType     OutputImageRegionType;
  typedef typename OutputImageType::PixelType      OutputImagePixelType;
  
  itkStaticConstMacro(InputImageDimension, unsigned int,
                      TInputImage::ImageDimension);
  itkStaticConstMacro(OutputImageDimension, unsigned int,
                      OutputImageType::ImageDimension);

  itkNewMacro(Self);  

  itkTypeMacro(BinaryFillholeImageFilter, 
               ImageToImageFilter);
  
  itkSetMacro(FullyConnected, bool);
  itkGetConstReferenceMacro(FullyConnected, bool);
  itkBooleanMacro(FullyConnected);

#ifdef ITK_USE_CONCEPT_CHECKING

  itkConceptMacro(InputOStreamWritableCheck,
                  (Concept::OStreamWritable<InputImagePixelType>));

#endif

  itkSetMacro(ForegroundValue, InputImagePixelType);
 
  itkGetMacro(ForegroundValue, InputImagePixelType);
 
  
protected:
  BinaryFillholeImageFilter();
  ~BinaryFillholeImageFilter() {};
  void PrintSelf(std::ostream& os, Indent indent) const;

  void GenerateInputRequestedRegion() ;

  void EnlargeOutputRequestedRegion(DataObject *itkNotUsed(output));
  
  void GenerateData();
  

private:
  BinaryFillholeImageFilter(const Self&);
  void operator=(const Self&);

  InputImagePixelType m_ForegroundValue;

  bool                m_FullyConnected;

} ; // end of class

template <class TInputImage>
BinaryFillholeImageFilter<TInputImage>
::BinaryFillholeImageFilter()
{
  m_FullyConnected = false;
  m_ForegroundValue = NumericTraits<InputImagePixelType>::max();
}

template <class TInputImage>
void 
BinaryFillholeImageFilter<TInputImage>
::GenerateInputRequestedRegion()
{

  Superclass::GenerateInputRequestedRegion();
  

  InputImagePointer input = const_cast<InputImageType *>(this->GetInput());
  if( input )
    {
    input->SetRequestedRegion( input->GetLargestPossibleRegion() );
    }
}


template <class TInputImage>
void 
BinaryFillholeImageFilter<TInputImage>
::EnlargeOutputRequestedRegion(DataObject *)
{
  this->GetOutput()
    ->SetRequestedRegion( this->GetOutput()->GetLargestPossibleRegion() );
}


template<class TInputImage>
void
BinaryFillholeImageFilter<TInputImage>
::GenerateData()
{

  this->AllocateOutputs();
  
  InputImagePixelType backgroundValue = NumericTraits<InputImagePixelType>::Zero;
  if ( m_ForegroundValue == backgroundValue )
    {
    backgroundValue = NumericTraits<InputImagePixelType>::max();
    }


  InputImagePointer markerPtr = InputImageType::New();
  markerPtr->SetRegions( this->GetInput()->GetRequestedRegion() );
  markerPtr->CopyInformation( this->GetInput() );
  markerPtr->Allocate();


  markerPtr->FillBuffer( m_ForegroundValue );

  ImageRegionExclusionIteratorWithIndex<TInputImage>
    markerBoundaryIt( markerPtr, this->GetInput()->GetRequestedRegion() );
  markerBoundaryIt.SetExclusionRegionToInsetRegion();

  markerBoundaryIt.GoToBegin();
  while ( !markerBoundaryIt.IsAtEnd() )
    {
    markerBoundaryIt.Set( backgroundValue );
    ++markerBoundaryIt;
    }
  
  ProgressAccumulator::Pointer progress = ProgressAccumulator::New();
  progress->SetMiniPipelineFilter(this);

  typename BinaryReconstructionByErosionImageFilter<TInputImage>::Pointer
    erode
    = BinaryReconstructionByErosionImageFilter<TInputImage>::New();
  erode->SetMarkerImage( markerPtr );
  erode->SetForegroundValue( m_ForegroundValue );
  erode->SetBackgroundValue( backgroundValue );
  erode->SetMaskImage( this->GetInput() );
  erode->SetFullyConnected( m_FullyConnected );
  erode->SetNumberOfThreads( this->GetNumberOfThreads() );
  progress->RegisterInternalFilter(erode,1.0f);

  erode->GraftOutput( this->GetOutput() );
  erode->Update();
  this->GraftOutput( erode->GetOutput() );
}


template<class TInputImage>
void
BinaryFillholeImageFilter<TInputImage>
::PrintSelf(std::ostream &os, Indent indent) const
{
  Superclass::PrintSelf(os, indent);

  os << indent << "ForegroundValue: " << static_cast<typename NumericTraits<InputImagePixelType>::PrintType>(m_ForegroundValue) << std::endl;
  os << indent << "FullyConnected: "  << m_FullyConnected << std::endl;
}

} // end namespace itk
